package com.example.common.recycler.decoration;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

/**
 * 可以用xml布局实现ItemDecoration的绘制，这对于复杂布局或以后需要调整的布局或布局内容变动频繁的非常有用
 * 适用于线性列表的ItemDecoration（例如聊天里的时间条显示）
 * 此类原理是采用LayoutInflate将xml解析为View对象，之后利用Bitmap进行绘制
 * 此类由于要经过测量布局,创建Bitmap等操作，性能较原生canvas绘制稍微有所下降，因此只适合较为复杂的布局或布局经常调整变化的情况
 */
@Deprecated//推荐使用BaseLayoutOrderItemDecoration
public abstract class BaseLayoutItemDecoration extends RecyclerView.ItemDecoration {
    //    Map<String, Bitmap> mBitmapCache = new HashMap<>();

    //重写sizeOf，并返回以kB为单位的缓存对象的大小
    LruCache<String, Bitmap> mBitmapCache;

    public View mItemView;//布局的view对象

    private int mItemHeight;//布局的高度

    private int mParentRestMeasureWidth;//recyclerView的剩余宽度
    private int mParentRestMeasureHeight;//recyclerView的剩余高度
    private Paint mDefaultPaint;

    public BaseLayoutItemDecoration() {
        initDefaultPaint();
        initBitmapCache();
    }

    /**
     * 用户需要在此返回想要绘制的布局资源id
     *
     * @return
     */
    public abstract int getItemDecorationLayoutResId();

    /**
     * 用户根据需要在此对布局进行初始化（即用户根据需要在这里进行findViewById）
     *
     * @param mItemView
     */
    public abstract void initItemDecorationLayoutViews(@NonNull View mItemView);

    @CallSuper
    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        if (mItemView == null) {
            setCurrentItemDecorationRestMeasureWidth(getRestMeasureWidth(parent));
            setCurrentItemDecorationRestMeasureHeight(getRestMeasureHeight(parent));
            mItemView = LayoutInflater.from(parent.getContext()).inflate(getItemDecorationLayoutResId(), parent, false);
            initItemDecorationLayoutViews(mItemView);
        }
    }

    //-------------------------开放如下方法---------------------------------------------

    /**
     * 获得指定view的剩余宽度（即可供子View的最大宽度）
     *
     * @param view 此view必须是已经测量过的，否则返回值并不准确
     * @return
     */
    public int getRestMeasureWidth(@NonNull View view) {
        return view.getMeasuredWidth() - view.getPaddingEnd() - view.getPaddingStart();
    }

    /**
     * 获得指定view的剩余高度（即可供子View的最大高度）
     *
     * @param view 此view必须是已经测量过的，否则返回值并不准确
     * @return
     */
    public int getRestMeasureHeight(@NonNull View view) {
        return view.getMeasuredHeight() - view.getPaddingTop() - view.getPaddingBottom();
    }

    /**
     * 如果recyclerview的itemview宽度不固定或者与itemview宽度不一致,则需要在ondraw中调用此进行更新
     * 实例：
     * public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
     * int count = parent.getChildCount();
     * for (int i = 0; i < count; i++) {
     * View child = parent.getChildAt(i);
     * setRestMeasureWidth(getRestMeasureWidth(child));
     * setRestMeasureHeight(getRestMeasureHeight(child));
     * // ...
     * }
     * }
     *
     * @param width
     */
    public void setCurrentItemDecorationRestMeasureWidth(int width) {
        mParentRestMeasureWidth = width;
    }

    /**
     * 同{@link BaseLayoutItemDecoration#setCurrentItemDecorationRestMeasureWidth}
     *
     * @param height
     */
    public void setCurrentItemDecorationRestMeasureHeight(int height) {
        mParentRestMeasureHeight = height;
    }

    /**
     * 获得ItemDecoration布局view对象
     *
     * @return
     */
    public View getItemDecorationView() {
        return mItemView;
    }

    /**
     * 获得默认的画笔
     *
     * @return
     */
    public Paint getDefaultPaint() {
        return mDefaultPaint;
    }

    /**
     * 获得TimeDecoration布局的高度
     * 注意此值有可能是变化的,因此在使用之前必须先调用notifyItemLayoutContentChange
     *
     * @return
     */
    public int getItemDecorationHeight() {
        return mItemHeight;
    }

    /**
     * 获得TimeDecoration布局的高度
     * 内部自动调用notifyItemLayoutContentChange
     * 注意notifyItemLayoutContentChange可能会触发测量，因此不能同时多次调用getCurrentItemHeight，否则可能会导致性能稍微下降
     *
     * @return
     */
    public int getCurrentItemDecorationHeight() {
        notifyItemDecorationChanged();
        return mItemHeight;
    }

    /**
     * 用户每次更新layout的内容都必须调用此方法
     */
    public void notifyItemDecorationChanged() {
        measureAndLayout();
    }

    /**
     * 对view进行测量
     *
     * @param view
     * @param restWidth  view的父剩余宽度
     * @param restHeight view的父剩余高度
     */
    public void measure(@NonNull View view, int restWidth, int restHeight) {
        int widthMeasureMode = View.MeasureSpec.EXACTLY;
        int heightMeasureMode = View.MeasureSpec.EXACTLY;
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        if (lp == null) {
            view.measure(View.MeasureSpec.makeMeasureSpec(restWidth, widthMeasureMode),
                    View.MeasureSpec.makeMeasureSpec(restHeight, heightMeasureMode));
            return;
        }
        if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {//WRAP_CONTENT需要AT_MOST的测量模式
            widthMeasureMode = View.MeasureSpec.AT_MOST;
        }
        if (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {//WRAP_CONTENT需要AT_MOST的测量模式
            heightMeasureMode = View.MeasureSpec.AT_MOST;
        }
        view.measure(View.MeasureSpec.makeMeasureSpec(restWidth, widthMeasureMode),
                View.MeasureSpec.makeMeasureSpec(restHeight, heightMeasureMode));
    }

    /**
     * 获得指定view的bitmap
     * 未使用缓存
     *
     * @param v
     * @return
     */
    @Deprecated//未复用缓存会导致bitmap的频繁创建与销毁，导致频繁gc,性能有所下降，推荐使用getBitmapWithCache
    @Nullable
    public Bitmap getBitmap(@NonNull View v) {
        int width = v.getWidth();
        int height = v.getHeight();
        if (width <= 0 || height <= 0) {
            return null;
        }
        Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        v.draw(c);
        return b;
    }

    /**
     * 获得指定view的bitmap
     * 会复用缓存
     *
     * @param v
     * @param cacheIndex 用于标记缓存（注意使用不当可能导致缓存穿透）
     * @return
     */
    @Nullable
    public Bitmap getBitmapWithCache(@NonNull View v, int cacheIndex) {
        int width = v.getWidth();
        int height = v.getHeight();
        if (width <= 0 || height <= 0) {
            return null;
        }
        String key = getBitmapCacheKey(cacheIndex, v.getWidth(), v.getHeight());
        Bitmap cache = mBitmapCache.get(key);
        if (cache == null || cache.isRecycled()) {
            cache = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
            mBitmapCache.put(key, cache);
//            Log.i("123456", "没有命中缓存:" + key);
        }

        Canvas c = new Canvas(cache);
        cache.eraseColor(Color.TRANSPARENT);//擦除原有信息
        v.draw(c);

        return cache;
    }

    /**
     * 绘制TimeDecoration布局
     *
     * @param c          绘制的画布
     * @param paint      用户指定绘制的画笔，如用户无特殊要求，可直接使用{@link BaseLayoutItemDecoration#getDefaultPaint()}
     * @param left       绘制的起始点坐标
     * @param top        绘制的起始点坐标
     * @param cacheIndex 用于标记缓存（注意使用不当可能导致缓存穿透）
     */
    public void drawLayoutItemDecorationWithCache(@NonNull Canvas c, @NonNull Paint paint, float left, float top, int cacheIndex) {
        Bitmap bitmap = getBitmapWithCache(mItemView, cacheIndex);
        if (bitmap != null) {
            c.drawBitmap(bitmap, left, top, paint);
        }

    }

    /**
     * 绘制TimeDecoration布局(不使用缓存)
     *
     * @param c
     * @param paint
     * @param left
     * @param top
     */
    @Deprecated//未复用缓存会导致bitmap的频繁创建与销毁，导致频繁gc,性能有所下降，推荐使用drawLayoutItemDecorationWithCache
    public void drawLayoutItemDecorationWithoutCache(@NonNull Canvas c, @NonNull Paint paint, float left, float top) {
        Bitmap bitmap = getBitmap(mItemView);
        if (bitmap != null) {
            c.drawBitmap(bitmap, left, top, paint);
            bitmap.recycle();
        }
    }

    //-------------------------------------------私有方法--------------------------------------
    private void initDefaultPaint() {
        mDefaultPaint = new TextPaint();
        mDefaultPaint.setAntiAlias(true);//抗锯齿
        mDefaultPaint.setDither(true);
    }

    private String getBitmapCacheKey(int cacheIndex, int width, int height) {
        return cacheIndex + "_" + width + "_" + height;
    }

    /**
     * 对TimeDecoration布局进行测量和布局
     * 此步操作后宽高信息才是准确的
     */
    private void measureAndLayout() {
        measure(mItemView, mParentRestMeasureWidth, mParentRestMeasureHeight);
        mItemHeight = mItemView.getMeasuredHeight();
        mItemView.layout(0, 0, mItemView.getMeasuredWidth(), mItemView.getMeasuredHeight());
    }

    private void initBitmapCache() {
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);//kB为单位
        //设置LruCache的缓存大小
        int cacheSize = maxMemory / 16;
        mBitmapCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                // 必须重写此方法来衡量每张图片的大小，默认返回图片数量。
                return value.getByteCount() / 1024;
            }

            //当LruCache的内存容量满的时候会调用,将oldValue的元素移除出来腾出空间给新的元素加入
            @Override
            protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
                if (oldValue != null && !oldValue.isRecycled()) {
                    oldValue.recycle();
                }
            }
        };
    }
}